/*
	File:    	APSRTPPacketHandler.c
	Package: 	Apple CarPlay Communication Plug-in.
	Abstract: 	n/a 
	Version: 	450.14
	
	Disclaimer: IMPORTANT: This Apple software is supplied to you, by Apple Inc. ("Apple"), in your
	capacity as a current, and in good standing, Licensee in the MFi Licensing Program. Use of this
	Apple software is governed by and subject to the terms and conditions of your MFi License,
	including, but not limited to, the restrictions specified in the provision entitled ”Public
	Software”, and is further subject to your agreement to the following additional terms, and your
	agreement that the use, installation, modification or redistribution of this Apple software
	constitutes acceptance of these additional terms. If you do not agree with these additional terms,
	please do not use, install, modify or redistribute this Apple software.
	
	Subject to all of these terms and in consideration of your agreement to abide by them, Apple grants
	you, for as long as you are a current and in good-standing MFi Licensee, a personal, non-exclusive 
	license, under Apple's copyrights in this original Apple software (the "Apple Software"), to use, 
	reproduce, and modify the Apple Software in source form, and to use, reproduce, modify, and 
	redistribute the Apple Software, with or without modifications, in binary form. While you may not 
	redistribute the Apple Software in source form, should you redistribute the Apple Software in binary
	form, you must retain this notice and the following text and disclaimers in all such redistributions
	of the Apple Software. Neither the name, trademarks, service marks, or logos of Apple Inc. may be
	used to endorse or promote products derived from the Apple Software without specific prior written
	permission from Apple. Except as expressly stated in this notice, no other rights or licenses, 
	express or implied, are granted by Apple herein, including but not limited to any patent rights that
	may be infringed by your derivative works or by other works in which the Apple Software may be 
	incorporated.  
	
	Unless you explicitly state otherwise, if you provide any ideas, suggestions, recommendations, bug 
	fixes or enhancements to Apple in connection with this software (“Feedback”), you hereby grant to
	Apple a non-exclusive, fully paid-up, perpetual, irrevocable, worldwide license to make, use, 
	reproduce, incorporate, modify, display, perform, sell, make or have made derivative works of,
	distribute (directly or indirectly) and sublicense, such Feedback in connection with Apple products 
	and services. Providing this Feedback is voluntary, but if you do provide Feedback to Apple, you 
	acknowledge and agree that Apple may exercise the license granted above without the payment of 
	royalties or further consideration to Participant.
	
	The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO WARRANTIES, EXPRESS OR 
	IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY 
	AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR
	IN COMBINATION WITH YOUR PRODUCTS.
	
	IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR CONSEQUENTIAL DAMAGES 
	(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
	PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION 
	AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
	(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE 
	POSSIBILITY OF SUCH DAMAGE.
	
	Copyright (C) 2017-2018 Apple Inc. All Rights Reserved. Not to be used or disclosed without permission from Apple.
*/

#if 0
#pragma mark == Includes ==
#endif

#include "CommonServices.h"
#include CF_RUNTIME_HEADER
#include LIBDISPATCH_HEADER
#include "StringUtils.h"

#include "APSRTPPacketHandler.h"
#include "SimpleQueue.h"
#include "LogUtils.h"
#include "DebugServices.h"
#include "AirPlayUtils.h"


//===========================================================================================================================
//	Logging
//===========================================================================================================================

ulog_define( APSRTPPacketHandler, kLogLevelNotice, kLogFlags_Default, "APSRTPPacketHandler", NULL );
#define aps_ulog( LEVEL, ... )			ulog( &log_category_from_name( APSRTPPacketHandler ), ( LEVEL ), __VA_ARGS__ )

#if 0
#pragma mark == Macros ==
#endif

#define PAYLOAD_TYPE(X)		RTPHeaderExtractPayloadType(X->pkt.rtp.header.m_pt)
#define SEQ_NUM(X)			ntohs(X->pkt.rtp.header.seq)
#define RTP_PAYLOAD(X)		X->pkt.rtp.payload


#if 0
#pragma mark == Structures ==
#endif

typedef struct
{	
	char	id[20];

	uint32_t *store;
	uint32_t maxSize;
	
	uint32_t head;
	uint32_t numElems;
} LocalQueue;


// From RFC2198:
//	  0                   1                    2                   3
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
//   |F|   block PT  |  timestamp offset         |   block length    |
//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

typedef uint32_t APSRFC2198PayloadHeader;

check_compile_time( sizeof( APSRFC2198PayloadHeader ) == 4 );

#define APSRFC2198PayloadHeaderInsertFNotLast( FIELDS, X )				( ( ( FIELDS ) & ~0x80000000 ) | ( ( ( X ) << 31 ) & 0x80000000 ) )
#define APSRFC2198PayloadHeaderExtractFNotLast( FIELDS )				( ( ( FIELDS ) >> 31 ) & 0x00000001 )

#define APSRFC2198PayloadHeaderInsertBlockPayloadType( FIELDS, X )		( ( ( FIELDS ) & ~0x7F000000 ) | ( ( ( X ) << 24 ) & 0x7F000000 ) )
#define APSRFC2198PayloadHeaderExtractBlockPayloadType( FIELDS )		( ( ( FIELDS ) >> 24 ) & 0x0000007F )

#define APSRFC2198PayloadHeaderInsertTimestampOffset( FIELDS, X )		( ( ( FIELDS ) & ~0x00FFFC00 ) | ( ( ( X ) << 10 ) & 0x00FFFC00 ) )
#define APSRFC2198PayloadHeaderExtractTimestampOffset( FIELDS )			( ( ( FIELDS ) >> 10 ) & 0x00003FFF )

#define APSRFC2198PayloadHeaderInsertBlockLength( FIELDS, X )			( ( ( FIELDS ) & ~0x000003FF ) | ( ( X ) & 0x000003FF ) )
#define APSRFC2198PayloadHeaderExtractBlockLength( FIELDS )				( ( FIELDS ) & 0x000003FF )

// RFC2198:
//    0 1 2 3 4 5 6 7
//   +-+-+-+-+-+-+-+-+
//   |F|   block PT  |
//   +-+-+-+-+-+-+-+-+

typedef uint8_t APSRFC2198PayloadHeaderLast;

check_compile_time( sizeof( APSRFC2198PayloadHeaderLast ) == 1 );

#define APSRFC2198PayloadHeaderLastInsertFNotLast( FIELDS, X )			( ( ( FIELDS ) & ~0x80 ) | ( ( ( X ) << 7 ) & 0x80 ) )
#define APSRFC2198PayloadHeaderLastExtractFNotLast( FIELDS )			( ( ( FIELDS ) >> 7 ) & 0x01 )

#define APSRFC2198PayloadHeaderLastInsertBlockPayloadType( FIELDS, X )	( ( ( FIELDS ) & ~0x7F ) | ( ( X ) & 0x7F ) )
#define APSRFC2198PayloadHeaderLastExtractBlockPayloadType( FIELDS )	( ( FIELDS ) & 0x7F )


typedef struct
{
	unsigned	blockPayLoadType	:7,		//Block payload type
				F_NotLast			:1,		//F flag - 0: Last block, 1: Block follows
				timeStampOffset		:14,	//TimeStamp offset for the block
				blockLen			:10;	//Length of the block in bytes, excluding header

} RFC2198ExtendedHeader;

#pragma pack(push, 1)
typedef struct
{
	unsigned	blockPayLoadType	:7,		//Block payload type
				F_NotLast			:1;		//F flag - 0: Last block, 1: Block follows
			
} RFC2198ExtendedHeaderLast;
#pragma pack(pop)

check_compile_time( sizeof( RFC2198ExtendedHeader )  == 4 );
check_compile_time( sizeof( RFC2198ExtendedHeaderLast )  == 1 );

static void APSRTPPacketHandlerWriteExtendedHeader( uint8_t *inPtr, const RFC2198ExtendedHeader *inHeader, uint8_t **outPtr );
static void APSRTPPacketHandlerReadExtendedHeader( const uint8_t *inPtr, RFC2198ExtendedHeader *outHeader, const uint8_t **outPtr );

struct APSRTPPacketHandlerPrivate
{
	CFRuntimeBase		base;									// CFRuntimeBase: must be first
	const char *		label;									// Label to use when logging

	//Common stuff
	uint32_t			maxNumPayloads;
	uint32_t			numPacketsInPool;
	
	
	
	//Stuff for composer
	struct
	{
		SimpleQueueRef		oldPackets;			// Queue for packets that will be written out as the next RTP packet.
		LocalQueue			oldPayloadLengths;	// Queue to mirror above packet's payload lengths.
		SimpleQueueRef		emptyPackets;		// Queue for empty packets.
		APSRTPSavedPacket	*packetArray;		// Pool of packets used throughout
	} composer;
	
	//Stuff for splitter
	struct
	{
		APSRTPSavedPacket*	currentPacket;				// Packet being currently processed
		uint32_t			numPacketsInCurrentMessage; // Number of packets available in the current message.
		uint32_t			numPacketsRead;				// State - number of packets read in the current packet.
		LocalQueue			recentSeqNums;				// Storage for recently-seen sequence numbers.
		SimpleQueueRef		emptyPackets;				// Queue for empty packets.
		APSRTPSavedPacket	*packetArray;				// Pool of packets used throughout
	} splitter;
};

#if 0
#pragma mark == Function Prototypes ==
#endif

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	APSRTPPacketHandlerFinalize
	@abstract	Destroys and deletes an instance of the RTP Redundancy Utility.
*/
void _APSRTPPacketHandlerFinalize( CFTypeRef inHandle );



#if 0
#pragma mark == Seq number queue implementation ==
#endif


//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	QueueInit
	@abstract	Initializes the sequence number queue.
*/
static void 
	QueueInit(
		LocalQueue*  inQueue,
		uint32_t	 inMaxSize,
		char* inId )
{	
	if( inMaxSize > 0)
	{
		inQueue->store = (uint32_t*) calloc ( sizeof(uint32_t), inMaxSize );
	}
	else
	{
		inQueue->store = NULL;
	}
	
	inQueue->head = 0;
	inQueue->numElems = 0;
	inQueue->maxSize = inMaxSize;
	
	strlcpy( inQueue->id, inId, sizeof( inQueue->id ) );
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	QueueClose
	@abstract	Frees the sequence number queue.
*/
static void
	QueueClose(
		LocalQueue* queue )
{
	if( queue->store )
	{
		free ( queue->store);
		queue->store = NULL;
	}
	queue->head = 0;
	queue->numElems = 0;
	queue->maxSize = 0;
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	QueueIsValPresent
	@abstract	Checks if given value is present in the queue.
*/
static bool 
	QueueIsValPresent(
		LocalQueue* queue,
		uint16_t	 inVal )
{
	uint32_t i;
	
	for( i = 0; i < queue->numElems; i++ )
	{
		uint32_t queueIndex = (queue->head + i) % queue->maxSize;
		if(queue->store[queueIndex] == inVal)
		{
			return true;
		}
	}

	return false;
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	QueueDeleteOldestIfFull
	@abstract	Deletes an element if the queue is full.
*/
static void
	QueueDeleteOldestIfFull(
		LocalQueue* queue )
{
	if( queue->numElems > 0 && 
		queue->numElems == queue->maxSize )
	{
		queue->head = (queue->head + 1) % queue->maxSize;
		queue->numElems--;
	}
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	QueueAddEntry
	@abstract	Pushes a new entry into the queue.
*/
static void
	QueueAddEntry(
		LocalQueue* queue,
		uint32_t	 inNewEntry )
{
	if( queue->numElems < queue->maxSize )
	{
		uint32_t newIndex = 0;
		
		newIndex = (queue->head + queue->numElems) % queue->maxSize;
		queue->store[newIndex] = inNewEntry;
		
		queue->numElems++;
	}
	else if( queue->maxSize > 0 )
	{
		aps_ulog(kLogLevelError, "Queue storage overflow\n");
	}
}


static uint32_t
	QueueGetTailIndex(
		LocalQueue*	queue )
{
	if( queue->numElems > 0)
	{
		return (queue->head + queue->numElems - 1) % queue->maxSize;
	}
	else
	{
		return queue->head;
	}
}

static uint32_t
	QueueGetNumElements( 
		LocalQueue*	queue )
{
	return queue->numElems;
}


static bool 
	QueueIsIndexInRange(
		LocalQueue *queue, 
		uint32_t inIdx)
{
	uint32_t tail = QueueGetTailIndex(queue);
	
	if( queue->head <= tail )
	{
		if( inIdx < queue->head || inIdx > tail)
		{
			aps_ulog(kLogLevelError, "%s:Queue index out of bounds; inIdx %d, head %d, numElems %d, tail %d\n", queue->id,
				 inIdx, queue->head, queue->numElems, tail );
				 
			return false;
		}
	}
	else if( inIdx > tail && inIdx < queue->head )
	{
		aps_ulog(kLogLevelError, "%s:Queue index out of bounds; inIdx %d, head %d, numElems %d, tail %d\n", queue->id,
				inIdx, queue->head, queue->numElems, tail );
		
		return false;
	}
	
	//All checks passed.
	return true;
}

static uint32_t
	QueueGetElementAtIndex( 
		LocalQueue* queue, 
		uint32_t inIdx ) 
{
	QueueIsIndexInRange( queue, inIdx );

	return queue->store[inIdx];
}

static bool
	QueueWalkBackGetIdx( 
		LocalQueue* queue,
		uint32_t*	inOutIdx )
{
	uint32_t idx = *inOutIdx;
	
	if( !QueueIsIndexInRange(queue, idx) )
		return false;
	
	//Cannot walk back beyond head.
	if( idx == queue->head )
	{
		//Next index is no longer valid.
		return false;
	}	
	
	//Wrap around.
	if( 0 == idx )
	{
		idx = queue->maxSize - 1;
	}
	else
	{
		//Normal walk back
		idx--;
	}
	
	*inOutIdx = idx;
	
	//New Index is still valid
	return true;
}

#if 0
#pragma mark == General ==
#endif


static OSStatus 
	APSRTPPacketHandlerCreatePoolAndQueue( 
		uint32_t		inNumPacketsInPool, 
		APSRTPSavedPacket **	outPacketArray, 
		SimpleQueueRef *		outEmptyPacketsQueue )
{
	OSStatus err = kNoErr;
	uint32_t i;

	APSRTPSavedPacket*	packetArray = NULL;
	SimpleQueueRef	emptyPacketQueue = NULL;

	//Array to hold old packets
	packetArray = ( APSRTPSavedPacket* ) calloc( sizeof( APSRTPSavedPacket ), inNumPacketsInPool );
	require_action( packetArray, bail, err = kNoMemoryErr);
	
	//Create the empty-packet queue
	err = SimpleQueueCreate( NULL, inNumPacketsInPool, &emptyPacketQueue );
	require_noerr_string( err, bail, "Unable to create empty queue\n" );
	
	//Populate the empty packet queue
	for( i=0; i < inNumPacketsInPool; i++ )
	{
		SimpleQueueEnqueue( emptyPacketQueue, &packetArray[i] );
	}
	
	//Populate output variables
	*outPacketArray = packetArray;
	*outEmptyPacketsQueue = emptyPacketQueue;
	
bail:
	return err;
}

static void _APSRTPPacketHandlerGetTypeID( void *outVal )
{
	CFTypeID *					outTypeID	= (CFTypeID*) outVal;
	static const CFRuntimeClass		class	=
	{
		0,								// version
		"APSRTPPacketHandler",			// className
		NULL,							// init
		NULL,							// copy
		_APSRTPPacketHandlerFinalize,	// finalize
		NULL,							// equal -- NULL means pointer equality.
		NULL,							// hash  -- NULL means pointer hash.
		NULL,							// copyFormattingDesc
		NULL,							// copyDebugDesc
		NULL,							// reclaim
		NULL							// refcount
	};
	
	*outTypeID = _CFRuntimeRegisterClass( &class );
	check( (*outTypeID) != _kCFRuntimeNotATypeID );
}

static CFTypeID APSRTPPacketHandlerGetTypeID( void )
{
	static dispatch_once_t		serviceInitOnce = 0;
	static CFTypeID				serviceTypeID = _kCFRuntimeNotATypeID;
	
	dispatch_once_f( &serviceInitOnce, &serviceTypeID, _APSRTPPacketHandlerGetTypeID );
	return( serviceTypeID );
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	RTPPacketHandlerCreate
	@abstract	Creates an instance of the RTP Redundancy Utility.
*/
OSStatus
	APSRTPPacketHandlerCreate(
		CFAllocatorRef		inAllocator,
		uint32_t			inMaxPayloadsPerPacket,
		const char *		inLabel,
		CFTypeRef*			outCtx )
{
	OSStatus err = kNoErr;	
	APSRTPPacketHandlerRef		ctx = NULL;
	uint32_t	len = 0;
	
	(void)inAllocator;
	
	len = sizeof( *ctx ) - sizeof( ctx->base );	
	ctx = (APSRTPPacketHandlerRef) _CFRuntimeCreateInstance( inAllocator, APSRTPPacketHandlerGetTypeID(), (CFIndex) len, NULL );
	require_action( ctx, bail, err = kNoMemoryErr );
	memset( ( (uint8_t *) ctx ) + sizeof( ctx->base ), 0, len );
	
	ctx->label = inLabel;

	// Initialize the common elements.
	ctx->maxNumPayloads = inMaxPayloadsPerPacket;
	
	
	//Initialize the Composer.	
	ctx->composer.oldPackets = NULL;
	ctx->composer.packetArray = NULL;
	ctx->composer.emptyPackets = NULL;
	
	// Allocate 2 extra packets - one to float for getting new packets during composition, one to use during splitting.
	ctx->numPacketsInPool = (int32_t) ctx->maxNumPayloads + 2;
	
	APSRTPPacketHandlerCreatePoolAndQueue( ctx->numPacketsInPool, &ctx->composer.packetArray, &ctx->composer.emptyPackets );
		
	//Create the old-packet queue
	if( ctx->maxNumPayloads > 1 )
	{	
		err = SimpleQueueCreate( NULL, (int32_t) ctx->maxNumPayloads - 1, &ctx->composer.oldPackets );
		require_noerr_string(err, bail, "Unable to create old packet queue\n");
		
		//The following is a mirror to hold packet lengths (for easy access - can be done by going through the above queue too).
		QueueInit( &ctx->composer.oldPayloadLengths, ctx->maxNumPayloads-1, "LenQ");
	}
	else
	{
		ctx->composer.oldPackets = NULL;
	}

	//Initialize the splitter elements
	
	APSRTPPacketHandlerCreatePoolAndQueue( ctx->numPacketsInPool, &ctx->splitter.packetArray, &ctx->splitter.emptyPackets );
	
	//Initialize storage for recently seen sequence numbers.
	QueueInit( &ctx->splitter.recentSeqNums, ctx->maxNumPayloads-1, "SeqQ");
	
	ctx->splitter.numPacketsInCurrentMessage = 0;
	ctx->splitter.numPacketsRead = 0;


bail:
	if( err )
	{
		CFReleaseNullSafe( ctx );
		ctx = NULL;
	}
	*outCtx = (CFTypeRef)ctx;
	return err;
}

static void
	APSRTPPacketHandlerDeletePoolAndQueue( 
		APSRTPPacketHandlerRef inCtx, 
		APSRTPSavedPacket **	inPacketArray, 
		SimpleQueueRef *		inEmptyPacketsQueue )
{
	(void) inCtx;
	CFReleaseNullSafe( *inEmptyPacketsQueue );
	*inEmptyPacketsQueue = NULL;
	
	if( *inPacketArray )
	{
		free( *inPacketArray );
		*inPacketArray = NULL;
	}
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	_APSRTPPacketHandlerFinalize
	@abstract	Destroys and deletes an instance of the RTP Redundancy Utility.
*/
void _APSRTPPacketHandlerFinalize( CFTypeRef inHandle )
{
	APSRTPPacketHandlerRef ctx =  (APSRTPPacketHandlerRef)inHandle;
	
	aps_ulog( kLogLevelTrace, "'%s': Closing packet handler\n", ctx->label );
	
	//Delete queues
	QueueClose(&ctx->composer.oldPayloadLengths);
	
	CFReleaseNullSafe(ctx->composer.oldPackets);
	ctx->composer.oldPackets = NULL;
	
	APSRTPPacketHandlerDeletePoolAndQueue( ctx, &ctx->composer.packetArray, &ctx->composer.emptyPackets );
	
	APSRTPPacketHandlerDeletePoolAndQueue( ctx, &ctx->splitter.packetArray, &ctx->splitter.emptyPackets );

	QueueClose(&ctx->splitter.recentSeqNums);
	
	ctx->maxNumPayloads = 0;
	ctx->numPacketsInPool = 0;
	
	ctx->label = NULL;
	
	aps_ulog( kLogLevelTrace,"Closing packet handler ... [done] \n");
}


#if 0
#pragma mark == Packet Composition ==
#endif


//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	APSRTPPacketHandlerGetEmptyPacketToRead
	@abstract	Returns an empty regular RTP packet from the splitter pool. Returns NULL if there were any errors.
*/

APSRTPSavedPacket* APSRTPPacketHandlerGetEmptyPacketToRead(CFTypeRef inCtx)
{
	APSRTPPacketHandlerRef ctx = (APSRTPPacketHandlerRef)inCtx;
	return (APSRTPSavedPacket*)SimpleQueueDequeue( ctx->splitter.emptyPackets );
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	APSRTPPacketHandlerGetEmptyPacketToCompose
	@abstract	Returns an empty regular RTP packet from the composer. Returns NULL if there were any errors.
*/

APSRTPSavedPacket* APSRTPPacketHandlerGetEmptyPacketToCompose(CFTypeRef inCtx)
{
	APSRTPPacketHandlerRef ctx = (APSRTPPacketHandlerRef)inCtx;
	return (APSRTPSavedPacket*)SimpleQueueDequeue( ctx->composer.emptyPackets );
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	APSRTPPacketHandlerReturnPacket
	@abstract	Sends an empty RTP packet back to the handler, if it wasn't used.
*/

#define IS_PART_OF( X, ARRAY, N )	(ARRAY <= X && X < ARRAY+N)

void APSRTPPacketHandlerReturnPacket(CFTypeRef inCtx, APSRTPSavedPacket* packet)
{
	APSRTPPacketHandlerRef ctx = (APSRTPPacketHandlerRef)inCtx;
	
	//Find out if this is a composer packet or splitter packet
	if( IS_PART_OF(packet, ctx->composer.packetArray, ctx->numPacketsInPool ) )
	{
		SimpleQueueEnqueue(ctx->composer.emptyPackets, packet);
	}
	else if (IS_PART_OF(packet, ctx->splitter.packetArray, ctx->numPacketsInPool ))
	{
		SimpleQueueEnqueue(ctx->splitter.emptyPackets, packet);
	}
	else
	{
		aps_ulog( kLogLevelError, "'%s': Unrecognized packet\n", ctx->label );
	}
}


// Walk through all packets, recording lengths.
// See where the total < packet length.

static uint32_t APSRTPPacketHandlerGetNumPacketsToAdd(
			CFTypeRef inCtx,
			APSRTPSavedPacket* inNewPacket,
			uint32_t inEncryptionOverhead )
{
	APSRTPPacketHandlerRef		ctx =  (APSRTPPacketHandlerRef)inCtx;
	uint32_t numStoredPackets = QueueGetNumElements( &ctx->composer.oldPayloadLengths );
	
	if( ctx->maxNumPayloads > 1 && numStoredPackets > 0 )
	{
		uint32_t maxLength = sizeof(inNewPacket->pkt);
		
		uint32_t payloadLen;
		uint32_t numOldPacketsToFill = 0;
		uint32_t idx = QueueGetTailIndex( &ctx->composer.oldPayloadLengths );
		uint32_t cumulativePacketLength = (uint32_t) ( inNewPacket->len + sizeof(RFC2198ExtendedHeaderLast) + inEncryptionOverhead );
		
		aps_ulog( kLogLevelChatty, "'%s': Tail is at %d; numStored %d; newPacketLen %d\n", ctx->label, idx, numStoredPackets, inNewPacket->len);
	
		//Scan through the queue backwards.
		do
		{
			uint32_t thisPacketRequires = 0;
		
			payloadLen = QueueGetElementAtIndex( &ctx->composer.oldPayloadLengths, idx );
			
			aps_ulog( kLogLevelChatty,"'%s': \tIdx: %d, len: %u\n", ctx->label, idx, payloadLen);
		
			thisPacketRequires = payloadLen + sizeof(RFC2198ExtendedHeader);
		
			if( cumulativePacketLength + thisPacketRequires >  maxLength )
				break;
			
			cumulativePacketLength += thisPacketRequires;
			numOldPacketsToFill++;
			
			aps_ulog( kLogLevelChatty,"'%s':\tNumPacketstofill: %d, cumulative so far: %d\n", ctx->label, numOldPacketsToFill, cumulativePacketLength);
			
		} while ( QueueWalkBackGetIdx(&ctx->composer.oldPayloadLengths, &idx ));
		
		return numOldPacketsToFill;
	}
	
	return 0;
}

//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	GetRedundantPacketType
	@abstract	Utility function to get the correct packet type value.
*/
static AirPlayStreamType GetRedundantPacketType( AirPlayStreamType inStreamType )
{
	AirPlayStreamType outStreamType = kAirPlayStreamType_Invalid;

	switch ( inStreamType ) 
	{
	  case kAirPlayStreamType_MainAudio:
		outStreamType = kAirPlayStreamType_MainAudioWithRedundancy;
		break;

	  case kAirPlayStreamType_AltAudio:
		outStreamType = kAirPlayStreamType_AltAudioWithRedundancy;
		break;

	  case kAirPlayStreamType_AuxInAudio:
		outStreamType = kAirPlayStreamType_AuxInAudioWithRedundancy;
		break;
		
	  case kAirPlayStreamType_AuxOutAudio:
	  	outStreamType = kAirPlayStreamType_AuxOutAudioWithRedundancy;
		break;

	  default:
	  	outStreamType = kAirPlayStreamType_Invalid;
		break;
	}
	
	return outStreamType;
}





//---------------------------------------------------------------------------------------------------------------------------
/*!	@function	RTPPacketHandlerComposeRTPPacket
	@abstract	Composes a complete RTP packet with redundant data and current data filled in.
*/
/*	Steps:
	* Insert all stored packets
	* Insert new packet
	* Delete oldest packet
	* Return composed packet.
*/
OSStatus APSRTPPacketHandlerComposeRTPPacket(
			CFTypeRef inCtx, 
			APSRTPSavedPacket* inNewPacket,
			uint32_t  inEncryptionOverhead, 
			APSRTPSavedPacket* outPacket,
			uint32_t* outPayloadOffset )
{

	APSRTPPacketHandlerRef		ctx =  (APSRTPPacketHandlerRef)inCtx;
	OSStatus				err = kNoErr;
	
	RFC2198ExtendedHeader	extendedHeader;
	uint8_t*				extendedHeaderWritePtr = NULL;
	uint8_t*				payload = NULL;
	uint32_t				totalNumOldPackets = SimpleQueueGetCount(ctx->composer.oldPackets);
	uint32_t				basePayloadOffset = 0;
	int32_t				newPayloadLen = 0;
	
	require(outPacket, bail);
			
	//Initialize local pointers to start of output RTP payload
	extendedHeaderWritePtr				= RTP_PAYLOAD(outPacket);
	
	//Initialize payload start
	payload			  = RTP_PAYLOAD(outPacket);
	basePayloadOffset = 0;
				  
	//----- Packet composition ---------
	//Use header info from the latest packet to fill header for output packet.
	outPacket->pkt.rtp.header = inNewPacket->pkt.rtp.header;
	outPacket->len = kRTPHeaderSize;	

	//Add additional headers and payload if we are using redundancy. If not, just copy out the current packet.
	//For later: Optimize to not copy when redundancy is turned off.
	if( ctx->maxNumPayloads > 1 )
	{
		uint32_t extraHeaderSize;
		uint32_t numOldPacketsToBeWritten = 0;
		uint32_t validPacketIdxStart = 0;
		uint32_t i;
		
		//Update the packet identifier for redundant data stream.
		outPacket->pkt.rtp.header.m_pt = GetRedundantPacketType( PAYLOAD_TYPE(inNewPacket) );
		
		//Find out how many packets can be written into this packet.
		numOldPacketsToBeWritten = APSRTPPacketHandlerGetNumPacketsToAdd(ctx, inNewPacket, inEncryptionOverhead );
		validPacketIdxStart = totalNumOldPackets - numOldPacketsToBeWritten;
		
		aps_ulog( kLogLevelChatty,"'%s': Num old packets to be written: %d; valid packet start: %d\n", ctx->label, numOldPacketsToBeWritten, validPacketIdxStart);
		
		//Account for headers for old packets + 1 byte header for last payload
		extraHeaderSize = (numOldPacketsToBeWritten * sizeof(RFC2198ExtendedHeader)) + 1;
		outPacket->len	   += extraHeaderSize;
		basePayloadOffset  += extraHeaderSize;
		payload				= RTP_PAYLOAD(outPacket) + basePayloadOffset;
			
		//Loop through all stored packets - additional header, payload.
		for( i=0; i < totalNumOldPackets; i++ )
		{
			APSRTPSavedPacket*			oldPacket = NULL;
			
			oldPacket = (APSRTPSavedPacket*)SimpleQueueDequeue( ctx->composer.oldPackets );
			require( oldPacket, bail );
			
			if( oldPacket->len < kRTPHeaderSize )
			{
				aps_ulog( kLogLevelError, "'%s': Invalid packet length %d", ctx->label, oldPacket->len );
				
				//Discard this packet and continue. This should never happen!
				APSRTPPacketHandlerReturnPacket( inCtx, oldPacket );
				continue;
			}
		
			//Write in only those packets that will fit - else just put them back into the queue.
			if( i >= validPacketIdxStart )
			{
				uint32_t oldPayloadLen = (uint32_t)oldPacket->len - kRTPHeaderSize;
				
				//Add extended header for old packet.
				extendedHeader.F_NotLast		= 1; //There is at least one packet coming after this one.
				extendedHeader.blockPayLoadType = PAYLOAD_TYPE(oldPacket);
				extendedHeader.timeStampOffset  = ntohl(outPacket->pkt.rtp.header.ts) - ntohl(oldPacket->pkt.rtp.header.ts); //Offset from current
				extendedHeader.blockLen		 	= oldPayloadLen; 
				
				APSRTPPacketHandlerWriteExtendedHeader( extendedHeaderWritePtr, &extendedHeader, &extendedHeaderWritePtr );

				//Copy payload for this packet
				memcpy( payload, RTP_PAYLOAD(oldPacket), oldPayloadLen );
				outPacket->len += oldPayloadLen;
				
				//Advance extended header and payload pointers to the next slots.
				payload += oldPayloadLen;
			}
			//Handle the old packets correctly.
			//The oldest one will get discarded. The others go back into the queue. Wish there was an easier way...
			//Since we stored the length of the queue outside this loop, this should be safe to do.
			if(0 == i && totalNumOldPackets == ctx->maxNumPayloads-1 ) //Queue is full, start dropping packets
			{
				APSRTPPacketHandlerReturnPacket( ctx, oldPacket );
				
				//Delete this element from the old-packet-length queue
				QueueDeleteOldestIfFull(&ctx->composer.oldPayloadLengths);
			}
			else
			{
				SimpleQueueEnqueue( ctx->composer.oldPackets, oldPacket ); //Back into the same queue.
			}
		}
		
		//All old packets written in now. Write the new packet.
		// Header for last packet looks like this:
		//	  0              
		//    0 1 2 3 4 5 6 7 
		//   +-+-+-+-+-+-+-+-+
		//   |F|   block PT  |
		//   +-+-+-+-+-+-+-+-+
		extendedHeader.F_NotLast = 0; 
		extendedHeader.blockPayLoadType = PAYLOAD_TYPE(inNewPacket);
		extendedHeader.timeStampOffset  = 0;
		extendedHeader.blockLen		 	= 0;

		APSRTPPacketHandlerWriteExtendedHeader( extendedHeaderWritePtr, &extendedHeader, &extendedHeaderWritePtr );
	}
	
	//Copy over new payload
	newPayloadLen = (int32_t) (inNewPacket->len - kRTPHeaderSize);
	if( newPayloadLen > 0 )
	{
		memcpy(payload, RTP_PAYLOAD(inNewPacket), newPayloadLen );
		outPacket->len += newPayloadLen;
	}
	*outPayloadOffset = basePayloadOffset;
	
	aps_ulog( kLogLevelChatty,"'%s': Final packet length: %d, withoutRTPHeader %d, payloadOffset %d\n", ctx->label, outPacket->len, outPacket->len - kRTPHeaderSize, basePayloadOffset );
	
	//--------- Packet composition complete---------//	
	
	if( ctx->maxNumPayloads > 1)
	{
		//Put new packet in the storage queue
		err = SimpleQueueEnqueue( ctx->composer.oldPackets, inNewPacket );
		require_noerr(err, bail);
		
		QueueAddEntry( &ctx->composer.oldPayloadLengths, ( (uint32_t)inNewPacket->len - kRTPHeaderSize) );
	}
	else
	{
		//Put new packet back in the empty queue
		APSRTPPacketHandlerReturnPacket( ctx, inNewPacket );
	}

bail:
	return err;

}



#if 0
#pragma mark == Packet decomposition ==
#endif

static Boolean HasRedundancy( APSRTPPacketHandlerRef ctx )
{
	Boolean supportsRedundancy = false;
	
	switch( PAYLOAD_TYPE( ctx->splitter.currentPacket ) )
	{
		case kAirPlayStreamType_MainAudioWithRedundancy:
		case kAirPlayStreamType_AltAudioWithRedundancy:
		case kAirPlayStreamType_AuxOutAudioWithRedundancy:
		case kAirPlayStreamType_AuxInAudioWithRedundancy:
			supportsRedundancy = true;
			break;
		default:
			supportsRedundancy = false;
			break;
	}

	return ( supportsRedundancy && ctx->maxNumPayloads > 1 );
}

static uint32_t 
	APSRTPPacketHandlerGetTotalNumPackets( 
		APSRTPPacketHandlerRef ctx )
{
	uint32_t numPackets = 1; //Default
	
	if( HasRedundancy( ctx ) )
	{
		RFC2198ExtendedHeader 	header;
		const uint8_t *			ptr		= RTP_PAYLOAD( ctx->splitter.currentPacket );
		
		APSRTPPacketHandlerReadExtendedHeader( ptr, &header, &ptr );
	
		//If the current pointer doesn't point to the last header, advance to the next one.
		while( header.F_NotLast )
		{
			numPackets++;
			
			if(numPackets > ctx->maxNumPayloads)
			{
				aps_ulog(kLogLevelError, "'%s': RFC2198: Last header not found in packet", ctx->label );
				return 0;
			}
			
			APSRTPPacketHandlerReadExtendedHeader( ptr, &header, &ptr );
		}
	}

	return numPackets;
}


static OSStatus 
	APSRTPPacketHandlerGetBasePayload( 
		APSRTPPacketHandlerRef ctx,
		uint8_t ** outPayload, 
		size_t *outPayloadLen )
{
	OSStatus				err = kNoErr;
	*outPayloadLen = 0;
	*outPayload = NULL;
	
	//If the payload type is not Redundancy, just return the payload from the regular RTP header.
	
	if( HasRedundancy( ctx ) )
	{		
		uint32_t extraHeaderSize = (ctx->splitter.numPacketsInCurrentMessage - 1) * sizeof(RFC2198ExtendedHeader) + 
									sizeof(RFC2198ExtendedHeaderLast);
		
		if(NULL != outPayload )
			*outPayload = RTP_PAYLOAD(ctx->splitter.currentPacket) + extraHeaderSize;
		
		if(NULL != outPayloadLen )
			*outPayloadLen = ctx->splitter.currentPacket->len - ( sizeof(RTPHeader) + extraHeaderSize );
	
	}
	else
	{
		if(NULL != outPayload )
			*outPayload = RTP_PAYLOAD(ctx->splitter.currentPacket);
		
		if(NULL != outPayloadLen )
			*outPayloadLen = ctx->splitter.currentPacket->len - sizeof(RTPHeader);
	}
	
	return err;
}


// Check sequence number continuity to catch packet drops.

static void	
	APSRTPPacketHandlerCheckSequenceNumberContinuity( APSRTPPacketHandlerRef inCtx )
{
	if( QueueGetNumElements( &inCtx->splitter.recentSeqNums ) > 0 )
	{
		//Most recent sequence number seen is at the tail
		uint32_t tailIdx = QueueGetTailIndex( &inCtx->splitter.recentSeqNums );
		uint16_t expectedSeqNum = (uint16_t) QueueGetElementAtIndex( &inCtx->splitter.recentSeqNums, tailIdx ) + 1;
		uint16_t curSeqNum = SEQ_NUM( inCtx->splitter.currentPacket );
		
		if( curSeqNum != expectedSeqNum )
		{
			aps_ulog( kLogLevelNotice, "RTP packet loss, %d packet(s) - expected sequence %u, received %u\n", 
						curSeqNum - expectedSeqNum - 1, 
						expectedSeqNum, curSeqNum );
		}
	}
}


OSStatus 
	APSRTPPacketHandlerReadNewMessage( 
		CFTypeRef inCtx, 
		APSRTPSavedPacket* inMessage )
{
	APSRTPPacketHandlerRef	ctx =  (APSRTPPacketHandlerRef)inCtx;
	OSStatus				err = kNoErr;

	ctx->splitter.currentPacket = inMessage;

	ctx->splitter.numPacketsRead = 0;
	
	ctx->splitter.numPacketsInCurrentMessage = APSRTPPacketHandlerGetTotalNumPackets(ctx);
	
	// Check sequence number continuity to catch and log packet drops.
	
	APSRTPPacketHandlerCheckSequenceNumberContinuity( ctx );
	
	aps_ulog( kLogLevelChatty,"'%s': New message seq %u; Num packets %d;  packetSize %u\n", ctx->label, SEQ_NUM(ctx->splitter.currentPacket), ctx->splitter.numPacketsInCurrentMessage, inMessage->len);

	return err;
}


static OSStatus 
	APSRTPPacketHandlerGetAAD(
			RTPHeader* inRTPHeaderPtr,
			bool isNewerOS,
			uint8_t** outAAD,
			size_t*	 outAADLength )
{
	if( isNewerOS )
	{
		*outAAD = &((uint8_t *)inRTPHeaderPtr)[offsetof( RTPHeader, ts )];
		*outAADLength = sizeof( inRTPHeaderPtr->ts ) + sizeof( inRTPHeaderPtr->ssrc );
	}
	else
	{
		*outAAD = (uint8_t *)inRTPHeaderPtr;
		*outAADLength = sizeof( RTPHeader );
	}
	
	return kNoErr;
}


OSStatus 
	APSRTPPacketHandlerDecryptCurrentMessage( 
		CFTypeRef inCtx, 
		void* inCryptor,
		bool	inNewOS)
{
	//解密音频数据
	APSRTPPacketHandlerRef	ctx =  (APSRTPPacketHandlerRef)inCtx;
	OSStatus				err = kNoErr;
	uint8_t*				payload = NULL;
	size_t					curPayloadLen = 0;
	size_t					newPayloadLen = 0;
	
	ChaChaPolyCryptor*		cryptor = (ChaChaPolyCryptor*) inCryptor;

	uint8_t *		aad			= NULL;
	size_t		aadLength	= 0;
	
	err = APSRTPPacketHandlerGetBasePayload( ctx,  &payload, &curPayloadLen );
	require_noerr( err, bail );
	
	
	if(cryptor->isValid )
	{
		APSRTPPacketHandlerGetAAD( &ctx->splitter.currentPacket->pkt.rtp.header, inNewOS, &aad, &aadLength );
		// The last 24 bytes of the payload are nonce and the auth tag. Both are LE. The rest of the payload is BE.
			
		require_action( ctx->splitter.currentPacket->len >= 24, bail, err = kUnknownErr );
		chacha20_poly1305_init_64x64( &cryptor->state, cryptor->key, &payload[ curPayloadLen - 8 ] );
		chacha20_poly1305_add_aad( &cryptor->state, aad, aadLength );
		newPayloadLen = chacha20_poly1305_decrypt( &cryptor->state, payload, curPayloadLen - 24, payload );
		newPayloadLen += chacha20_poly1305_verify( &cryptor->state, &payload[ newPayloadLen ], &payload[ curPayloadLen - 24 ], &err );
		require_noerr( err, bail );
		require_action( newPayloadLen == curPayloadLen - 24, bail, err = kInternalErr );
		
		// Increment nonce - for debugging only
		
		LittleEndianIntegerIncrement( cryptor->nonce, sizeof( cryptor->nonce ) );
		
		//Update the packet length with the difference of the new and the old lengths.
		ctx->splitter.currentPacket->len += newPayloadLen - curPayloadLen;
	}
	
bail:
	return err;
}



static void
	APSRTPPacketHandlerGetExtendedHeaderOfPacket(
		APSRTPPacketHandlerRef	ctx, 
		uint32_t				inPacketIdx,
		RFC2198ExtendedHeader*  outExtendedHeader )
{
	uint8_t* extendedHeaderPtr = RTP_PAYLOAD( ctx->splitter.currentPacket ) + ( inPacketIdx * sizeof( APSRFC2198PayloadHeader ) );
	
	APSRTPPacketHandlerReadExtendedHeader( extendedHeaderPtr, outExtendedHeader, NULL );
}

static uint32_t
	APSRTPPacketHandlerGetTimeStampOfPacket(
		APSRTPPacketHandlerRef	ctx, 
		uint32_t				inPacketIdx )
{
	RFC2198ExtendedHeader	extendedHeader;
	uint32_t outTimeStamp  = 0;
	
	if( inPacketIdx < ctx->splitter.numPacketsInCurrentMessage-1 )
	{
		APSRTPPacketHandlerGetExtendedHeaderOfPacket( ctx, inPacketIdx, &extendedHeader );

		outTimeStamp = ntohl( ctx->splitter.currentPacket->pkt.rtp.header.ts ) - //Base timestamp
							extendedHeader.timeStampOffset;
	}
	else
	{
		outTimeStamp = ntohl( ctx->splitter.currentPacket->pkt.rtp.header.ts );
	}

	return outTimeStamp;
}

static uint16_t
	APSRTPPacketHandlerGetSeqNumOfPacket(
		APSRTPPacketHandlerRef	ctx, 
		uint32_t				inPacketIdx )
{	
	uint16_t outSeqNum = SEQ_NUM(ctx->splitter.currentPacket) -	//Base seq number
						 ctx->splitter.numPacketsInCurrentMessage +
						 inPacketIdx + 
						 1; //Account for zero-base math
						 
	return outSeqNum;
}

static bool
	APSRTPPacketHandlerNewPacketCheck(
		APSRTPPacketHandlerRef	ctx, 
		uint32_t				inPacketIdx )
{
	if( HasRedundancy( ctx ) )
	{
		uint16_t curSeqNum = APSRTPPacketHandlerGetSeqNumOfPacket(ctx, inPacketIdx);
		return !QueueIsValPresent(&ctx->splitter.recentSeqNums, curSeqNum);
	}
	else
	{
		return true;
	}
}

static OSStatus 
	APSRTPPacketHandlerGetHeaderOfPacket(
		APSRTPPacketHandlerRef	ctx, 
		uint32_t				inPacketIdx, 
		RTPHeader *				outHeader)
{
	OSStatus	err = kNoErr;
	RFC2198ExtendedHeader extHeader;
	
	require_string(inPacketIdx < ctx->splitter.numPacketsInCurrentMessage, bail, "Accessing packet outside current message");
	
	*outHeader = ctx->splitter.currentPacket->pkt.rtp.header;
	
	//Use extended header information if this is redundant audio
	if( HasRedundancy( ctx ) )
	{
		outHeader->ts	= htonl( APSRTPPacketHandlerGetTimeStampOfPacket(ctx, inPacketIdx) );
		outHeader->seq	= htons( APSRTPPacketHandlerGetSeqNumOfPacket(ctx, inPacketIdx) );
		
		APSRTPPacketHandlerGetExtendedHeaderOfPacket( ctx, inPacketIdx, &extHeader );
		outHeader->m_pt = extHeader.blockPayLoadType;
	}
	
bail:	
	return err;
}

static OSStatus 
	APSRTPPacketHandlerGetPayloadOfPacket(
		APSRTPPacketHandlerRef	ctx, 
		uint32_t				inPacketIdx, 
		uint8_t **				outPayload,
		size_t *				outPayloadLen)
{
	OSStatus	err = kNoErr;
	uint32_t i;
	
	require_string(inPacketIdx < ctx->splitter.numPacketsInCurrentMessage, bail, "Accessing packet outside current message");
	
	if( !HasRedundancy( ctx ) )
	{	
		*outPayload = RTP_PAYLOAD(ctx->splitter.currentPacket);
		*outPayloadLen = ctx->splitter.currentPacket->len - sizeof(RTPHeader);
	}
	else
	{
		RFC2198ExtendedHeader extHeader;
		uint8_t	* payload = NULL;
		size_t  basePayloadLenDummy = 0;
		
		APSRTPPacketHandlerGetBasePayload(ctx, &payload, &basePayloadLenDummy);
		
		for( i=0; i < inPacketIdx; i++)
		{
			APSRTPPacketHandlerGetExtendedHeaderOfPacket( ctx, i, &extHeader );
			payload += extHeader.blockLen;
		}
		
		*outPayload = payload;
		
		//Not the last packet in the message
		if( inPacketIdx < ctx->splitter.numPacketsInCurrentMessage-1 )
		{
			APSRTPPacketHandlerGetExtendedHeaderOfPacket( ctx, inPacketIdx, &extHeader );
			*outPayloadLen = extHeader.blockLen;
		}
		//Last packet in the message.
		else 
		{
			//Bytes traversed so far
			uint32_t bytesTraversed = (uint32_t)(payload - ctx->splitter.currentPacket->pkt.bytes);
			
			*outPayloadLen = ctx->splitter.currentPacket->len - bytesTraversed;
		}
	}
	
bail:	
	return err;
}



bool 
	APSRTPPacketHandlerGetNextPacket( 
		CFTypeRef inCtx, 
		RTPDisjointPacket *outRtpPacket )
{
	APSRTPPacketHandlerRef	ctx =  (APSRTPPacketHandlerRef)inCtx;
	
	uint32_t				curPacketIdx = 0;
	bool					packetAvailable = false;
	
	if ( ctx->splitter.numPacketsRead < ctx->splitter.numPacketsInCurrentMessage )
	{
		do
		{
			curPacketIdx = ctx->splitter.numPacketsRead;
			ctx->splitter.numPacketsRead++;

			if( APSRTPPacketHandlerNewPacketCheck(ctx, curPacketIdx) )
			{
				APSRTPPacketHandlerGetPayloadOfPacket(ctx, curPacketIdx, &outRtpPacket->payload, &outRtpPacket->payloadLen );
		
				APSRTPPacketHandlerGetHeaderOfPacket(ctx, curPacketIdx, &outRtpPacket->header);
			
				QueueDeleteOldestIfFull(&ctx->splitter.recentSeqNums);
				QueueAddEntry(&ctx->splitter.recentSeqNums, ntohs(outRtpPacket->header.seq));
				
				packetAvailable = true;
				break;
			}
		} while (ctx->splitter.numPacketsRead < ctx->splitter.numPacketsInCurrentMessage);

		// Once we have read and sent out all available packets, release the packet BBuf.
		// This can perhaps be done in a separate call.

		if( ctx->splitter.numPacketsRead == ctx->splitter.numPacketsInCurrentMessage )
		{
			APSRTPPacketHandlerReturnPacket(ctx, ctx->splitter.currentPacket);
			ctx->splitter.currentPacket = NULL;
		}
	}
	
	return packetAvailable;
}

static void APSRTPPacketHandlerReadExtendedHeader( const uint8_t *inPtr, RFC2198ExtendedHeader *outHeader, const uint8_t **outPtr )
{
	require( outHeader, bail );
	memset( outHeader, 0, sizeof( RFC2198ExtendedHeader ) );
	
	const APSRFC2198PayloadHeaderLast	payloadHeaderLast	= *(APSRFC2198PayloadHeaderLast *) inPtr;
	
	outHeader->F_NotLast			= APSRFC2198PayloadHeaderLastExtractFNotLast( payloadHeaderLast );
	outHeader->blockPayLoadType		= APSRFC2198PayloadHeaderLastExtractBlockPayloadType( payloadHeaderLast );
	
	if( outHeader->F_NotLast )
	{
		const APSRFC2198PayloadHeader	payloadHeader		= ntoh32( *(APSRFC2198PayloadHeader *) inPtr );
		
		outHeader->timeStampOffset	= APSRFC2198PayloadHeaderExtractTimestampOffset( payloadHeader );
		outHeader->blockLen			= APSRFC2198PayloadHeaderExtractBlockLength( payloadHeader );
	}
	
	if( outPtr )
	{
	
		*outPtr = inPtr + ( outHeader->F_NotLast ? sizeof( APSRFC2198PayloadHeader ) : sizeof( APSRFC2198PayloadHeaderLast ) );
	}
	
bail:
	return;
}


static void APSRTPPacketHandlerWriteExtendedHeader( uint8_t *inPtr, const RFC2198ExtendedHeader *inHeader, uint8_t **outPtr )
{
	require( inHeader, bail );
	require( inPtr, bail );
	
	if( inHeader->F_NotLast )
	{
		APSRFC2198PayloadHeader		fields	= 0;
		
		fields = APSRFC2198PayloadHeaderInsertFNotLast( fields, inHeader->F_NotLast );
		fields = APSRFC2198PayloadHeaderInsertBlockPayloadType( fields, inHeader->blockPayLoadType );
		fields = APSRFC2198PayloadHeaderInsertTimestampOffset( fields, inHeader->timeStampOffset );
		fields = APSRFC2198PayloadHeaderInsertBlockLength( fields, inHeader->blockLen );
		
		*(APSRFC2198PayloadHeader *) inPtr = hton32( fields );
	}
	else
	{
		APSRFC2198PayloadHeaderLast	fields	= 0;
		
		fields = APSRFC2198PayloadHeaderLastInsertFNotLast( fields, inHeader->F_NotLast );
		fields = APSRFC2198PayloadHeaderLastInsertBlockPayloadType( fields, inHeader->blockPayLoadType );
		
		*(APSRFC2198PayloadHeaderLast *) inPtr = fields;
	}
	
	if( outPtr )
	{
		*outPtr = inPtr + ( inHeader->F_NotLast ? sizeof( APSRFC2198PayloadHeader ) : sizeof( APSRFC2198PayloadHeaderLast ) );
	}
	
bail:
	return;
}

